﻿namespace KLF.Lib.Browser.DOM.HTML
{
    #region Using Statements

    using System;
    using System.Linq;
    using System.Text;
    using System.Collections;
    using System.Collections.Generic;
    using System.Reflection;

    #endregion

    /// <summary>
    /// <code>
    /// HTMLWrappedCollection<HTMLImageElement>
    /// </code>
    /// </summary>
    /// <typeparam name="CollectionT"></typeparam>
    public class HTMLWrappedCollection<CollectionT>
    {
        #region Constructors

        /// <summary>
        /// Construct a new HTMLWrappedCollection.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        internal HTMLWrappedCollection(object source, string propertyName)
        {
            _PropertyName = propertyName;
            _Target = source;
            _TargetType = source.GetType();
            _PropertyInfo = _TargetType.GetProperty(propertyName,
                BindingFlags.NonPublic | 
                BindingFlags.Public | 
                BindingFlags.GetProperty |
                BindingFlags.Instance);

            if (_PropertyInfo == null)
                throw new ArgumentException(String.Format(
                    "Object '{0}' does not have property '{1}'",
                    _TargetType.ToString(), propertyName));

            if (_PropertyInfo.PropertyType != typeof(IEnumerable<CollectionT>))
                throw new ArgumentException(String.Format(
                    "Property '{0}' does not have return type '{1}'",
                    propertyName, typeof(CollectionT)));

            _PropertyTypeInfo = typeof(CollectionT);

            if (_PropertyTypeInfo.GetProperty(HTMLConstants.HTML_ATT_NAME) == null)
                _PropertyTypeInfo = null;

            var result = Items;
        }

        #endregion

        #region Attributes

        /// <summary>
        /// Returns the underlying collection.
        /// </summary>
        internal List<CollectionT> Items
        {
            get
            {
                IEnumerable<CollectionT> result = 
                    (IEnumerable<CollectionT>)_PropertyInfo.GetValue(_Target, null);

                return new List<CollectionT>(result);
            }
        }

        /// <summary>
        /// Contains information about the target property.
        /// </summary>
        private PropertyInfo _PropertyInfo { get; set; }

        /// <summary>
        /// Contains information about the inner type.
        /// </summary>
        private Type _PropertyTypeInfo { get; set; }

        /// <summary>
        /// The name of the targetted property.
        /// </summary>
        private string _PropertyName { get; set; }

        /// <summary>
        /// The instance of the object from which to get data.
        /// </summary>
        private object _Target { get; set; }

        private Type _TargetType { get; set; }

        #endregion

        #region DOM Level 2 HTML Compliance

        /// <summary>
        /// This method retrieves a node specified by ordinal index. Nodes are
        /// numbered in tree order (depth-first traversal order). 
        /// </summary>
        /// 
        /// <param name="index">The index of the node to be fetched. The index origin is 0</param>
        /// 
        /// <returns>The Node at the corresponding position upon success. A 
        /// value of null is returned if the index is out of range.</returns>
        public object item(int index)
        {
            var currentState = Items;

            if (index > -1 && index < currentState.Count)
                return Items[index];
            else
                return null;
        }

        /// <summary>
        /// This attribute specifies the length or size of the list.
        /// </summary>
        public int length
        {
            get
            {
                return Items.Count;
            }
        }

        /// <summary>
        /// This method retrieves a node specified by ordinal index. Nodes are
        /// numbered in tree order (depth-first traversal order). 
        /// </summary>
        /// 
        /// <param name="index"></param>
        /// 
        /// <returns></returns>
        public object this[int index]
        {
            get
            {
                return item(index);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public object this[string index]
        {
            get
            {
                var currentState = Items;

                foreach (CollectionT inst in currentState)
                {
                    if (_PropertyTypeInfo != null)
                    {
                        string instName = (string)_PropertyTypeInfo
                            .GetProperty(HTMLConstants.HTML_ATT_NAME)
                            .GetValue(inst, null);

                        if (index == instName)
                            return inst;
                    }
                }
                return null;
            }
        }

        #endregion
    }
}
